package gov.fnal.xpath2ldap;

import org.w3c.dom.Document;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
import org.apache.commons.cli.CommandLine;

import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPSearchConstraints;
import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPSchema;
import com.novell.ldap.LDAPObjectClassSchema;
import com.novell.ldap.LDAPSearchResults;

import java.io.OutputStream;
import java.util.HashMap;

/**
 *
 * User: tdh
 * Date: Jan 5, 2009
 * Time: 12:56:04 PM
 *
 */
public class XMLAdapter {

    private CommandLine cmdline;
    private String hostname = null;            /* mandatory */
    private String base = null;            /* mandatory */
    private int port = LDAPConnection.DEFAULT_PORT;
    private LDAPConnection l_conn = null;
    private LDAPSchema lFullSchema = null;
    public static String OCLASS_STR = "objectclass=";
    public static HashMap<String, String> portmap = new HashMap<String, String>();

    {
        portmap.put("ldap", "2170");
    }

   public XMLAdapter(CommandLine cmdline) {
        this.cmdline=cmdline;
        String[] args = cmdline.getArgs();

        String host_url = cmdline.getOptionValue("H");
        String[] slashslashsplit = host_url.split("//");
        String protocol = slashslashsplit[0];
        protocol = protocol.substring(0, protocol.length()-1);
        if(!"ldap".equals(protocol)) {
            System.err.println("Protocol found from parsing " + host_url + " is not one of: ldap");
            System.exit(1);
        }
        if(slashslashsplit.length<2) {
            System.err.println("Could not find hostname from parsing " + host_url);
            System.exit(1);
        }
        String host_and_port = slashslashsplit[1];
        String[] colonsplit = host_and_port.split(":");
        String hostname = colonsplit[0];
        String port_str;
        if(colonsplit.length<2) {
            //System.err.println("Could not find hostname from parsing " + host_url);
            port_str = portmap.get(protocol);
        } else {
            port_str = colonsplit[1];
        }
        int port=0;
        try {
            port=Integer.decode(port_str).intValue();
        } catch (NumberFormatException nfe) {
            System.err.println("Could not decode port from " + host_url + " to a number");
            System.exit(1);
        }
        this.hostname = hostname;
        this.port = port;
        this.base = cmdline.getOptionValue("b");
        //getLDAPSchema();
    }

    public XMLAdapter(String hostname, int port) {
        this.hostname = hostname;
        this.port = port;
        getLDAPSchema();
    }

    public CommandLine getCommandLine() {
        return cmdline;
    }

    public Document constructXML(String filter, String[] attrs, Document doc) {
        doc = search(base, LDAPConnection.SCOPE_SUB, filter, attrs, doc);
        //doc = search("Mds-Vo-name=SBGrid-Harvard-Exp,Mds-Vo-name=local,o=grid", LDAPConnection.SCOPE_SUB, filter, attrs, doc);
        //doc = search("Mds-Vo-name=Gridplexus,Mds-Vo-name=local,o=grid", LDAPConnection.SCOPE_SUB, filter, attrs, doc);
        return doc;
    }

    public Document search(String s_baseDN, int scope, String s_filter, String[] s_attributes, Document lDoc) {
        LDAPSearchResults lResults;
        try {
            l_conn = new LDAPConnection();

            l_conn.connect(hostname, port);

            LDAPSearchConstraints lCons = new LDAPSearchConstraints();
            lCons.setMaxResults(0);
            lCons.setBatchSize(0);

            lResults = l_conn.search(
                            s_baseDN,
                            scope,
                            s_filter,
                            s_attributes,
                            false,
                            lCons);

            // Reading thread may not be done yet
            while(!l_conn.areMessagesComplete()) {
               try { Thread.sleep(250);} catch (Exception e) {}
            }

            l_conn.disconnect();

            lDoc  = DSMLDOMConverter.resultToDOM(lResults, lDoc);
        } catch (LDAPException le) {
            System.err.println("LDAP Exception");
            le.printStackTrace();
            return null;
        }

        return lDoc;
    }

    public boolean isObjectclass(String classname) {
        LDAPSchema schema = getLDAPSchema();
        return schema.getObjectClassSchema(classname)!=null;
    }

    public boolean isStructuralObjectclass(String classname) {
        LDAPSchema schema = getLDAPSchema();
        LDAPObjectClassSchema oclass_schema = schema.getObjectClassSchema(classname);
        return oclass_schema!=null && oclass_schema.getType()==LDAPObjectClassSchema.STRUCTURAL;
    }

    public boolean isAuxiliaryObjectclass(String classname) {
        LDAPSchema schema = getLDAPSchema();
        LDAPObjectClassSchema oclass_schema = schema.getObjectClassSchema(classname);
        return oclass_schema!=null && oclass_schema.getType()==LDAPObjectClassSchema.AUXILIARY;
    }

    public boolean hasAtribute(String classname, String attrname) {
        LDAPSchema schema = getLDAPSchema();
        LDAPObjectClassSchema oclass_schema = schema.getObjectClassSchema(classname);
        if (oclass_schema==null) return false;
        String[] attributes = oclass_schema.getRequiredAttributes();
        if(attributes!=null) {
            for(String attr : attributes) {
                if(attr.equals(attrname)) return true;
            }
        }
        attributes = oclass_schema.getOptionalAttributes();
        if(attributes!=null) {
            for(String attr : attributes) {
                if(attr.equals(attrname)) return true;
            }
        }
        return false;
    }

    public Document mergeXML() {
        return null;
    }

    public LDAPSchema getLDAPSchema() {
        if(lFullSchema==null) lFullSchema = getSchema();
        return lFullSchema;
    }

    public LDAPSchema getSchema() {
        LDAPSchema lFullSchema=null;

        try {
            l_conn = new LDAPConnection();

            l_conn.connect(hostname, port);

            String schemaDN = l_conn.getSchemaDN();
            lFullSchema = l_conn.fetchSchema(schemaDN);

            l_conn.disconnect();
        } catch (LDAPException lExcept) {
            System.err.println("LDAPException while fetching schema: " + lExcept.resultCodeToString());
        }


        return lFullSchema;
    }


    public static String testDOM3LS(Document docToSerialize) throws Exception {

        // Get a simple DOM Document that will be serialized.

        // Get a factory (DOMImplementationLS) for creating a Load and Save object.
        org.w3c.dom.ls.DOMImplementationLS impl =
                (org.w3c.dom.ls.DOMImplementationLS)
                        org.w3c.dom.bootstrap.DOMImplementationRegistry.newInstance().getDOMImplementation("LS");

        // Use the factory to create an object (LSSerializer) used to
        // write out or save the document.
        org.w3c.dom.ls.LSSerializer writer = impl.createLSSerializer();
        org.w3c.dom.DOMConfiguration config = writer.getDomConfig();
        config.setParameter("format-pretty-print", Boolean.TRUE);

        // Use the LSSerializer to write out or serialize the document to a String.
        String serializedXML = writer.writeToString(docToSerialize);
        return serializedXML;
    }

    public static void serialize(Document doc, OutputStream out) throws Exception {//TO
        //Get a DOM Implementation LS Object
        DOMImplementationLS DOMiLS = getDOMImplementationLS(doc);
        LSSerializer LSS = DOMiLS.createLSSerializer();

        //get a LSOutput object
        LSOutput LSO = DOMiLS.createLSOutput();
        LSO.setByteStream(out);

        //do the serialization
        boolean ser = LSS.write(doc, LSO);
    }
    
    public static String serialize(Document doc) throws Exception {
        //Get a DOM Implementation LS Object
        DOMImplementationLS DOMiLS = getDOMImplementationLS(doc);
        LSSerializer LSS = DOMiLS.createLSSerializer();

        //do the serialization
        return LSS.writeToString(doc);
    }

    public static DOMImplementationLS getDOMImplementationLS(Document doc) {
        DOMImplementationLS DOMiLS=null;

        if((doc.getFeature("Core","3.0")!=null)&&
                (doc.getFeature("LS","3.0")!=null))
        {
            DOMiLS=(DOMImplementationLS)(doc.getImplementation()).
                    getFeature("LS","3.0");
        }else
        {
            System.out.println("[DOM Load and Save unsupported]");
            System.exit(1);
        }

        return DOMiLS;
    }

}
